home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Overview / Signals / UFailure.h < prev    next >
Encoding:
Text File  |  1994-11-18  |  10.5 KB  |  249 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    Exception handling for MPW Pascal, MacApp and MPW C
  6. #
  7. #    UFailure (aka Signals) - “Exceptional code, with a few exceptions.”
  8. #
  9. #    UFailure.h    -    C header
  10. #
  11. #    Copyright © 1985-1988 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    1.00                11/88
  15. #                1.01                06/92
  16. #
  17. #    Components:    CTestSignal.c        November 1, 1988
  18. #                CTestSignal.make    November 1, 1988
  19. #                PTestSignal.p        November 1, 1988
  20. #                PTestSignal.make    November 1, 1988
  21. #                UFailure.p            November 1, 1988
  22. #                UFailure.h            November 1, 1988
  23. #                UFailure.incl.p        November 1, 1988
  24. #                UFailure.a            November 1, 1988
  25. #
  26. #    UFailure (or Signals) is a set of exception handling routines suitable for
  27. #    use with MacApp, MPW C, and MPW Pascal. It is a jazzed-up version of the MacApp
  28. #    UFailure unit. There is a set of C interfaces to it as well.
  29. #
  30. ------------------------------------------------------------------------------*/
  31.  
  32.  
  33. /*
  34. Theory:
  35.     See the “theory of operation” comments below, and the commentary in Technical
  36.     Note #88.
  37.     
  38.     *** New ***
  39.     The warnings in technote 88 about not using CatchSignal in an expression, etc. no
  40.     longer apply. The exact state of the routine is now restored (all non-scratch
  41.     registers are preserved). Also, a fixed-size nonrelocatable block is now used, so
  42.     there is a limit on the depth of nested CatchSignals. This may be adjusted by
  43.     changing the constant SigBlockSize in UFailure.a and rebuilding the unit. For most
  44.     applications, though, the default depth of eight will be more than sufficient.
  45.     
  46.     Note:
  47.     *** There is a special c version of CatchFailures called CatchCFailures. If you use it
  48.     you will be required to call FreeSignal or Success explicitly (as with CatchFailures in
  49.     Pascal). There is no c version of CatchFailures because c doesn’t support the concept
  50.     of nested procedures. CatchCFailures provides the same functionality. ***
  51.     
  52.     *** You can use either the technote mechanism or the MacApp one in your program. It
  53.     is OK to call Signal if CatchFailures was used, and OK to call Failure if
  54.     CatchSignal was used, i.e. the two schemes may be freely intermixed. ***
  55.     
  56.         
  57.     What this version adds to the MacApp mechanism is two things:
  58.         1.
  59.             Exception records (FailInfo) are taken from a special heap block when
  60.             you use CatchSignal, so you don’t have to pass a FailInfo record as a
  61.             parameter.
  62.         2.
  63.             You can take advantage of stack frames and use of Signals as described
  64.             in Technical Note #88, without being forced to make explicit calls to
  65.             FreeSignal. This unit is fully upwards compatible with the 1986 version
  66.             of the technote.
  67.             
  68.     This includes all of the MacApp calls from the original UFailure, of course.
  69.  
  70.                     T H E O R Y   O F   O P E R A T I O N
  71.  
  72.     This unit implements the MacApp and Signal failure mechanisms.
  73.  
  74.     The failure mechanism is built around exception handlers.  An exception
  75.     handler is a piece of code, generally local to some other routine, that is
  76.     called when a failure occurs and takes action to handle the failure.
  77.     An exception handler is of the form
  78.  
  79.         PROCEDURE ExceptionHandler (error: OSErr; message: LONGINT);
  80.                                     or
  81.         the handler may consist of execution returning to the point of a
  82.         CatchSignal
  83.  
  84.     where error is the error that caused the failure, and message identifies
  85.     the error message that may be displayed.  Consider a routine that opens
  86.     a file, reads its contents, and closes the file.  If a failure occured
  87.     while reading the file, an exception handler would be needed to close the
  88.     file, as the rest of the routine would not be executed.  (See TestCignal
  89.     and TestSignal for examples of how to use these calls.)
  90.  
  91.     References to exception handlers are kept in the FailInfo record. The
  92.     exception handlers form a linked-list via the nextInfo field of FailInfo.
  93.     The linked list is a stack since new exception handlers are added to the
  94.     front of the list.
  95.  
  96.     New exception handlers are added to the stack with the CatchSignal
  97.     function or CatchFailures procedure. They are removed from the stack
  98.     automatically (CatchSignal) or via the Success procedure (if CatchFailures
  99.     was used). In general you call CatchFailures/CatchSignal to post an exception
  100.     handler when an error the application should handle might occur. You
  101.     can then manually pop the last handler off the stack with FreeSignal or
  102.     Success, if necessary. You may want to pop the handler (even if you used
  103.     CatchSignal) after the possibility of a specific type of error no longer
  104.     exists. Subsequent exceptions would then be passed to a previous (more
  105.     general) handler.
  106.     
  107.     Any failure detected within the limits of the CatchFailures/CatchSignal call
  108.     results in the execution of the exception handler.  (Failure does
  109.     not have to occur in the same routine as your call to CatchFailures.
  110.     The failure may occur in any routine called after the catch but before the
  111.     implicit/explicit pop of the handler.)
  112.  
  113.     When MacApp (or your code) determines that a failure has occured, it
  114.     calls Failure or Signal.  As a convenience, several procedures are provided
  115.     to check for standard kinds of failures and call Failure if needed.
  116.     These procedures are:
  117.  
  118.         FailNIL            Calls Failure if its parameter is NIL.
  119.         FailOSErr        Calls Failure if its parameter is not noErr.
  120.         FailMemError    Calls Failure if MemError returns other than noErr.
  121.         FailResError    Calls Failure if ResError returns other than noErr.
  122.  
  123.     When the exception is raised, execution of the routine that signalled is
  124.     terminated and the exception handler at the top of the stack is popped.
  125.     For each routine that was called after the handler was posted
  126.     to the stack, execution is terminated as though from an EXIT statement,
  127.     and the exception handler is called.  It generally cleans up for the
  128.     routine in which it is nested.  Upon completion the next exception handler
  129.     is popped from the stack, repeating the process.
  130.  
  131.     The error causing the failure, and a message code is passed to the handler.
  132.     
  133.     MacApp Specifics
  134.     For MacApp, the last exception handler on the stack is the one in
  135.     TApplication.PollEvent.  It calls TApplication.ShowError, which calls
  136.     ErrorAlert, which decodes the message and displays an alert.  Your exception
  137.     handlers may set the message code to one more specific to your application
  138.     by calling FailNewMessage at the end of your exception handler.
  139.     FailNewMessage changes the message only if the current one is non-zero.
  140.     This has the effect of allowing those exception handlers closest to the
  141.     source of the error to set the message.
  142.  
  143.     One last note about exception handlers:  It is possible for an exception
  144.     handler to terminate exception processing by using a non-local GOTO to
  145.     jump back into the routine in which the exception handler is nested.  This
  146.     is how MacApp keeps the application running when a failure occurs.  The
  147.     last exception handler on the stack, in TApplication.PollEvent, uses a
  148.     GOTO to continue event processing.
  149. */
  150.  
  151. typedef struct {
  152.     long            regs[11];            /* D3-D7/A2-A7 */
  153.     short            error;
  154.     long            message;
  155.     long            failA6;
  156.     long            failPC;
  157.     Ptr                nextInfo;
  158.     /* this is needed for compatibility with MacApp debugging */
  159.     long            whoPC;
  160.     /* these are added for USignalFailure unit use */
  161.     short            whatSignals;
  162.     /* this is used to keep the old stack frame return address */
  163.     long            sigFRet;
  164. } FailInfo, *PFailInfo;
  165.  
  166.  
  167. typedef pascal void (*HandlerFuncPtr)(short error, long message);
  168.  
  169.  
  170. /* Call the following initialization routine before your other initializations (InitGraf, etc.)-
  171.  in other words as early as you can in the application. */
  172.  
  173. extern pascal void InitUFailure();
  174.     /* Allocates the heap block for CatchSignals and initializes the global
  175.         variables used by the unit. C programs must use this instead of InitSignals. */
  176.  
  177.  
  178. extern pascal short CatchSignal();
  179.     /* Until the function which encloses this call returns, this will catch
  180.         subsequent Signal calls, returning the code passed to Signal.  When
  181.         CatchSignal is encountered initially, it returns a code of zero.  These
  182.         calls may "nest"; i.e. you may have multiple CatchSignals in one function.
  183.         Each nested CatchSignal call uses 72 bytes of heap space.
  184.         If you signal with Failure and pass in a non-zero message you should use
  185.         CatchCFailures instead so you have a way of getting at the message. */
  186.  
  187. extern pascal void FreeSignal();
  188.     /* This undoes the effect of the last CatchSignal/CatchFailures.  A Signal will then invoke
  189.         the CatchSignal prior to the last one. */
  190.         
  191. extern pascal void Signal(short code);
  192.     /* Returns control to the point of the last CatchSignal/CatchFailures.  The program will
  193.         then behave as though that CatchSignal had returned with the code parameter
  194.         supplied to Signal. If CatchCFailures is catching, the message parameter will be 0. */
  195.         
  196. extern pascal void SignalMessage(short code, long message);
  197.     /* Returns control to the point of the last CatchSignal. If CatchCFailures is catching,
  198.         the message parameter will be returned. */
  199.  
  200.         
  201.         
  202. /*------------------------------------+
  203. |    MacApp routines                    |
  204. +------------------------------------*/
  205.  
  206.  
  207.  
  208. pascal long  BuildMessage(short lowWord, short highWord)
  209.             = 0x2E9F;    /*  MOVE.L        (A7)+,(A7)     */
  210.     /* Takes the 2 integers and combines them into a LONGINT.  Note that the
  211.         low-order word is the first parameter. */
  212.  
  213. extern pascal void CatchCFailures(FailInfo *fi, HandlerFuncPtr handler);
  214.     /* Call this to set up an exception handler. This pushes your handler onto
  215.         a stack of exception handlers. */
  216.  
  217. extern pascal void Failure(short error, long message);
  218.     /* Call this to signal a failure.  Control will branch to the most recent
  219.         exception handler, which will be popped off the handler stack. */
  220.  
  221. extern pascal void FailMemError();
  222.     /* If MemError != noErr then call Failure(MemError, 0);  If you are using
  223.         assembler, then you should just test the return code from the Memory
  224.         Manager in DO by calling FailOSErr.    (See the discussion of MemError in
  225.         Inside Macintosh.) */
  226.  
  227. extern pascal void FailResError();
  228.     /* If ResError != noErr then call Failure(ResError, 0); (See Inside Macintosh.) */
  229.  
  230. extern pascal void FailNewMessage(short error, long oldMessage, long newMessage);
  231.     /* This does:
  232.         if (oldMessage == 0)
  233.             Failure(error, newMessage);
  234.         else
  235.             Failure(error, oldMessage);
  236.      */
  237.  
  238. extern pascal void FailNIL(Ptr p);
  239.     /* Call this with a pointer/handle; this signals Failure(memFullErr, 0) iff
  240.         the pointer is nil. */
  241.  
  242. extern pascal void FailOSErr(short error);
  243.     /* Call this with an OSError; signals Failure(error, 0) iff error != noErr. */
  244.  
  245.  
  246. extern pascal void Success(FailInfo *fi);
  247.     /* Call this when you want to de-install your exception handler (pop 1
  248.         element off the handler stack). */
  249.